package cc.zstart.generator.mybatis.plugin;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import cc.zstart.generator.mybatis.mapper.ExtendsXmlMapperGenerator;
import cc.zstart.generator.mybatis.service.ServiceImplGenerator;
import cc.zstart.generator.mybatis.service.ServiceInterfaceGenerator;
import cc.zstart.generator.mybatis.mapper.ExtendsJavaMapperGenerator;
import cc.zstart.generator.mybatis.modify.ExtendsBaseRecordGenerator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.dom.DefaultJavaFormatter;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.exception.ShellException;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.mybatis.generator.internal.ObjectFactory;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;

/**
 * @desc <p>
 * </p>
 * @author DaiJiangGuo
 * @date 2015年5月11日下午9:38:46
 */
public class AmanPlugin extends PluginAdapter {
    private DefaultShellCallback shellCallback = new DefaultShellCallback(false);
    private AmanPluginConfig apc;

    /**
     * 插件是否生效执行
     */
    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public void initialized(IntrospectedTable introspectedTable) {
        apc = new AmanPluginConfig(context, introspectedTable, properties);
    }

    /**
     * serviceInterface file、serviceImpl file生成
     */
    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        List<GeneratedJavaFile> list = new ArrayList<GeneratedJavaFile>();

        // service 生成
        if(apc.service_gen){
            try {
                ServiceInterfaceGenerator serviceInterfaceGenerator = new ServiceInterfaceGenerator(apc);
                List<CompilationUnit> compilationUnits = serviceInterfaceGenerator.getCompilationUnits();
                for (CompilationUnit compilationUnit : compilationUnits) {
                    Interface interfaze = (Interface) compilationUnit;
                    GeneratedJavaFile gjf = new GeneratedJavaFile(interfaze, apc.targetProjectService, new DefaultJavaFormatter());

                    File interfaceDirectory = shellCallback.getDirectory(apc.targetProjectService, interfaze.getType().getPackageName());
                    File interfaceFile = new File(interfaceDirectory, interfaze.getType().getShortName() + ".java");
                    if (interfaceFile.exists()) {
                        // 修改文件
                        System.out.println(interfaceFile.getName() + "\t存在，不生成");
                    } else {
                        list.add(gjf);
                    }

                    ServiceImplGenerator serviceImplGenerator = new ServiceImplGenerator(apc, (Interface) interfaze);
                    List<CompilationUnit> impls = serviceImplGenerator.getCompilationUnits();
                    for (CompilationUnit impl : impls) {
                        GeneratedJavaFile gjfImpl = new GeneratedJavaFile(impl, apc.targetProjectService, new DefaultJavaFormatter());

                        File classDirectory = shellCallback
                                .getDirectory(apc.targetProjectService, impl.getType().getPackageName());
                        File classFile = new File(classDirectory, impl.getType().getShortName() + ".java");
                        if (classFile.exists()) {
                            // 修改文件
                            System.out.println(classFile.getName() + "\t存在，不生成");
                        } else {
                            list.add(gjfImpl);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        return list;
    }

    /**
     * java Mapper file 生成
     */
    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        ExtendsJavaMapperGenerator mapperGenerator = new ExtendsJavaMapperGenerator(apc);

//        mapperGenerator.addQueryByMapMethod(interfaze, introspectedTable, "queryByMap");
//        mapperGenerator.addQueryByEntityMethod(interfaze, introspectedTable, "queryByEntity");
//        mapperGenerator.addQueryByPagerMethod(interfaze, introspectedTable, "queryByPager");

        try {
            String targetProject = context.getJavaClientGeneratorConfiguration().getTargetProject();
            String targetPackage = context.getJavaClientGeneratorConfiguration().getTargetPackage();
            String fileName = interfaze.getType().getShortName() + ".java";

            File directory = shellCallback.getDirectory(targetProject, targetPackage);
            File targetFile = new File(directory, fileName);
            if (targetFile.exists()) {
                System.out.println(targetFile.getName() + "\t存在，不生成");
                return false;
            } else {// 生成文件
                return true;
            }
        } catch (ShellException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * sqlMap file生成
     */
    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        try {
            String targetProject = context.getSqlMapGeneratorConfiguration().getTargetProject();
            String targetPackage = introspectedTable.getMyBatis3XmlMapperPackage();
            String fileName = introspectedTable.getMyBatis3XmlMapperFileName();

            File directory = shellCallback.getDirectory(targetProject, targetPackage);
            File targetFile = new File(directory, fileName);
            if (targetFile.exists()) {
                System.out.println(targetFile.getName() + "\t存在，不生成");
                
                List<IntrospectedColumn> dbCols = apc.introspectedTable.getAllColumns();
                List<IntrospectedColumn> needAddCols = new ArrayList();
                List<IntrospectedColumn> needUpdCols = new ArrayList();
                List<IntrospectedColumn> needDelFields = new ArrayList();

                Class<?> cls = ObjectFactory.externalClassForName(apc.modelType.getFullyQualifiedName());
                Field[] fs = cls.getDeclaredFields();

                for(IntrospectedColumn dbCol : dbCols){
                    boolean fieldExist = false;
                    for(Field f : fs){
                        if(StringUtils.equalsIgnoreCase(f.getName(), dbCol.getJavaProperty())){//存在
                            fieldExist = true;
                            String colTypeName = dbCol.getFullyQualifiedJavaType().getShortName();
                            String fieldTypeName = f.getType().getSimpleName();
                            if(!StringUtils.equals(colTypeName, fieldTypeName)){//类型不同
                                needUpdCols.add(dbCol);
                            } else {
                                Column a = f.getAnnotation(Column.class);
                                if(a == null){//没有注解
                                    needUpdCols.add(dbCol);
                                } else if(!StringUtils.equals(a.name(), dbCol.getActualColumnName())){//注解列名不同
                                    needUpdCols.add(dbCol);
                                }
                            }
                            break;
                        }
                    }
                    if(!fieldExist){
                        needAddCols.add(dbCol);
                    }
                }
                for(Field f : fs){
                    Column columnAnnotation = f.getAnnotation(Column.class);
                    if(columnAnnotation != null){
                        boolean dbColExist = false;
                        for(IntrospectedColumn dbCol : dbCols){
                            if(StringUtils.equalsIgnoreCase(f.getName(), dbCol.getJavaProperty())){//存在
                                dbColExist = true;
                                break;
                            }
                        }
                        if(!dbColExist){
                            String colName = StringUtils.defaultIfBlank(columnAnnotation.name(), propertyToDbcol(f.getName()));
                            
                            IntrospectedColumn needDelField = new IntrospectedColumn();
                            needDelField.setActualColumnName(colName);
                            needDelField.setJavaProperty(f.getName());
                            
                            needDelFields.add(needDelField);
                        }
                    }
                }
                
                ExtendsXmlMapperGenerator xmlMapperGenerator = new ExtendsXmlMapperGenerator(apc);
                if(CollectionUtils.isNotEmpty(needAddCols)){
                    xmlMapperGenerator.addColumns(needAddCols, targetFile, sqlMap, introspectedTable);
                }
                if(CollectionUtils.isNotEmpty(needUpdCols)){
                    xmlMapperGenerator.updColumns(needUpdCols, targetFile, sqlMap, introspectedTable);
                }
                if(CollectionUtils.isNotEmpty(needDelFields)){
                    xmlMapperGenerator.delColumns(needDelFields, targetFile, sqlMap, introspectedTable);
                }
                return false;
            } else {// 生成文件
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    public static String propertyToDbcol(String property){
        if(StringUtils.isBlank(property)){
            return property;
        }
        return property.replaceAll("[A-Z]", "_$0").toLowerCase();
    }

    /**
     * sqlMapDocument生成
     */
    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        ExtendsXmlMapperGenerator mapperGenerator = new ExtendsXmlMapperGenerator(apc);

//        mapperGenerator.addQueryByMapMethod(document, "queryByMap");
//        mapperGenerator.addQueryByEntityMethod(document, "queryByEntity");
//        mapperGenerator.addQueryByPagerMethod(document, "queryByPager");

        return true;
    }

    /**
     * EntityMode file 生成
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        try {
            String targetPackage = context.getJavaModelGeneratorConfiguration().getTargetPackage();
            String targetProject = context.getJavaModelGeneratorConfiguration().getTargetProject();
            String fileName = topLevelClass.getType().getShortName() + ".java";

            File directory = shellCallback.getDirectory(targetProject, targetPackage);
            File targetFile = new File(directory, fileName);
            if (targetFile.exists()) {
                ExtendsBaseRecordGenerator g = new ExtendsBaseRecordGenerator(apc);

                List<IntrospectedColumn> dbCols = apc.introspectedTable.getAllColumns();
                List<IntrospectedColumn> pkCols = apc.introspectedTable.getPrimaryKeyColumns();
                List<IntrospectedColumn> needAddCols = new ArrayList();
                List<IntrospectedColumn> needUpdCols = new ArrayList();
                List<String> needDelFields = new ArrayList();
                
                Class<?> cls = ObjectFactory.externalClassForName(apc.modelType.getFullyQualifiedName());
                Field[] fs = cls.getDeclaredFields();
                for(IntrospectedColumn dbCol : dbCols){
                    boolean fieldExist = false;
                    for(Field f : fs){
                        if(StringUtils.equalsIgnoreCase(f.getName(), dbCol.getJavaProperty())){//存在
                            fieldExist = true;
                            String colTypeName = dbCol.getFullyQualifiedJavaType().getShortName();
                            String fieldTypeName = f.getType().getSimpleName();
                            if(!StringUtils.equals(colTypeName, fieldTypeName)){//类型不同
                                needUpdCols.add(dbCol);
                            } else {
                                Column a = f.getAnnotation(Column.class);
                                if(a == null){//没有注解
                                    needUpdCols.add(dbCol);
                                } else if(!StringUtils.equals(a.name(), dbCol.getActualColumnName())){//注解列名不同
                                    needUpdCols.add(dbCol);
                                } else {//主键
                                    if(apc.isPkCol(pkCols, dbCol) && dbCol.isAutoIncrement()){
                                        if(f.getAnnotation(GeneratedValue.class) == null){//应该设置自动增长生成 没有设置
                                            needUpdCols.add(dbCol);
                                        }
                                    } else {
                                        if(f.getAnnotation(GeneratedValue.class) != null){//不应该设置自动增长生成 设置
                                            needUpdCols.add(dbCol);
                                        }
                                    }
                                    dbCol.isAutoIncrement();
                                }
                            }
                            break;
                        }
                    }
                    if(!fieldExist){
                        needAddCols.add(dbCol);
                    }
                }
                for(Field f : fs){
                    Column columnAnnotation = f.getAnnotation(Column.class);
                    if(columnAnnotation != null){
                        boolean dbColExist = false;
                        for(IntrospectedColumn dbCol : dbCols){
                            if(StringUtils.equalsIgnoreCase(f.getName(), dbCol.getJavaProperty())){//存在
                                dbColExist = true;
                                break;
                            }
                        }
                        if(!dbColExist){
                            needDelFields.add(f.getName());
                        }
                    }
                }
                if(CollectionUtils.isNotEmpty(needAddCols)){
                    g.addColumns(needAddCols, targetFile, topLevelClass, introspectedTable);
                }
                if(CollectionUtils.isNotEmpty(needUpdCols)){
                    g.updTypeColumns(needUpdCols, targetFile, topLevelClass, introspectedTable);
                }
                if(CollectionUtils.isNotEmpty(needDelFields)){
                    g.delColumns(needDelFields, targetFile, topLevelClass);
                }
                
                System.out.println(targetFile.getName() + "\t存在，不生成,添加属性:["+needAddCols.size()+"], 修改属性["+needUpdCols.size()+"],删除属性["+needDelFields.size()+"]");
                return false;
            } else {// 生成文件
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
